#pragma once

#include < vcclr.h >
#include <msclr\marshal.h>
#include "engine.h"
#include "entity/entity.h"
namespace interop
{
	using namespace System;
	using namespace System::ComponentModel;
	using namespace System::Collections;
	using namespace msclr::interop;
	using namespace System::Data;
	using namespace System::Drawing;
	using namespace System::Drawing::Design;
	using namespace System::Windows::Forms::Design;
	using namespace System::Windows::Forms;

	inline void String_to_string(String^ Str,char* ch)
	{
#if 1
		marshal_context ^ context = gcnew marshal_context();
		const char* str = context->marshal_as<const char*>(Str);
		strcpy(ch,str);
		delete context;
#else
		using namespace Runtime::InteropServices;
		const wchar_t* chars = 
			(const wchar_t*)(Marshal::StringToHGlobalUni(Str)).ToPointer();
		wstring os = chars;
		string s(os.begin(),os.end());

		strcpy(ch,s.c_str());
		Marshal::FreeHGlobal(IntPtr((void*)chars));
	
#endif
	}

	public ref class PropertyNode
	{
	public:
		property_t* gamepropdesc;
		String^ prop_name;
		PropertyNode(property_t* i_gpd,String^ pn):gamepropdesc(i_gpd),prop_name(pn){}
	};


	struct node
	{
		node(const char* n,property_t* pd=NULL):prop_desc(pd){strcpy(name,n);}
		char name[192];
//		string name;
		property_t* prop_desc;
//		vector<node> children;
		fixedvector<node*,192> children;
		void* operator new(size_t);
		void operator delete(void*);
		void pushdown(const char* subname,property_t* pd)
		{
			unsigned pos=0;
			char eleje[192];
			for (; subname[pos] && subname[pos]!='.'; ++pos)
			{
				eleje[pos]=subname[pos];
			}
			eleje[pos]=0;
			if (!subname[pos])
				pos=-1;
				
			node* it=NULL;
			for (unsigned n=0; n<children.size(); ++n)
			{
				if (strcmp(children[n]->name,eleje)==0)
				{
					it=children[n];
					break;
				}
			}
			node* child;
			if (it==NULL)
			{
				children.push_back(new node(eleje,pd));
				child=children.back();
			}
			else
			{
				child=it;
			}

			if (pos!=-1)
				child->pushdown(subname+pos+1,pd);
		}
	};


	void get_property_from_grid(entity_t* i_Node,property_t* i_GameProperty,System::Object^ val);


	public ref class MyPropertyTable: public PropertyGridEx::PropertyTable
	{
	public:
		virtual String^ ToString() override
		{
			return gcnew String("(...)");
		}

		Hashtable^ UserData;
		MyPropertyTable^ parent;
 		entity_t* entity;
		
		MyPropertyTable(MyPropertyTable^ p,entity_t* ent)
		{
			entity=ent;
			parent=p;
			UserData = gcnew Hashtable();
		}
	public:

		virtual void OnSetValue(PropertyGridEx::PropertySpecEventArgs^ e) override;
	};

	public struct Point3D
	{
	public:
		float x,y,z;

		Point3D(){}
		Point3D(float _x,float _y,float _z):x(_x),y(_y),z(_z){}
		Point3D(const float3& v)
		{
			x=v.x;
			y=v.y;
			z=v.z;
		}
	};

	public ref class Transform
	{
	public:
		float4x4* mtx;
		Transform():mtx(new float4x4){}
		Transform(const float4x4& _mtx):mtx(new float4x4(_mtx)){}
		!Transform(){if (mtx){delete mtx;mtx=NULL;}}
		~Transform(){if (mtx){delete mtx;mtx=NULL;}}

		void SetRotation(float y,float p,float r)
		{
			mtx->from_rotation(y,p,r);
		}

		void SetTranslation(float x,float y, float z)
		{
			mtx->translate().set(x,y,z);
		}

		Point3D GetTranslation()
		{
			return Point3D(mtx->translate());
		}
	};

	public ref class EntityTreeNode : System::Windows::Forms::TreeNode
	{
	public:
		entity_t* entity;
		MyPropertyTable^ bag;
		bool locked;

		EntityTreeNode^ clone(EntityTreeNode^ parentNode)
		{
		    entity_t* e=entity->clone();
			 EntityTreeNode^ newNode= gcnew EntityTreeNode(gcnew String(e->name.c_str())+"["+(gcnew String(e->get_metaobject()->get_typename().c_str()))+"]",e);

			 parentNode->Nodes->Add(newNode);
			 parentNode->entity->addchild(e);

			 

			 return newNode;
		}

		void setText()
		{
			Text=gcnew String(entity->name.c_str())+"["+(gcnew String(entity->get_metaobject()->get_typename().c_str()))+"]";
		}

		EntityTreeNode(System::String^ text,entity_t* i_Node):TreeNode(text),entity(i_Node)
		{
		}

		void AddChild(EntityTreeNode^ ch)
		{
			entity->addchild(ch->entity);
		}
		void AddAfter(EntityTreeNode^ ch)
		{
			entity->add_after(ch->entity);
		}
		void AddBefore(EntityTreeNode^ ch)
		{
			entity->add_before(ch->entity);
		}

		Transform^ GetWorldMatrix()
		{
			return gcnew Transform(entity->getworldmtx());
		}

		void SetWorldMatrix(Transform^ t)
		{
			entity->setworldmtx(*t->mtx);
		}

		Transform^ GetInitialWorldMatrix()
		{
			return gcnew Transform(entity->getinitialworldmtx());
		}

		Transform^ GetInitialLocalMatrix()
		{
			return gcnew Transform(entity->initiallocalmtx);
		}

		void SetInitialWorldMatrix(Transform^ wm)
		{
			entity->setinitialworldmtx(*wm->mtx);
		}

		int getElemHeight()
		{
			int h=0;

			TreeNode^ ptr=this;
			if (ptr!=nullptr)
			{
				++h;
				ptr=ptr->Parent;
			}

			return (h << 16)+this->Index;
		}

		void OnChangeProperty(String^ propname)
		{
			char ch[1024];
			String_to_string(propname,ch);
			entity->onchangeproperty(stringhash::get(ch));
		}

		bool IsActive()
		{
			return entity->isactive();
		}

		void InitRecursive()
		{
			entity->initrecursive();
		}

		void ExitRecursive()
		{
			entity->exitrecursive();
		}

	};




	public ref class float4x4prop:public MyPropertyTable
	{
	public:
		virtual String^ ToString() override
		{
			String^ s = gcnew String("");
			float3 pos,rot,scale;
			pos.set((float)(*this)["X"],(float)(*this)["Y"],(float)(*this)["Z"]);
			rot.set((float)(*this)["PITCH"],(float)(*this)["YAW"],(float)(*this)["ROLL"]);
			scale.set((float)(*this)["SCALE X"],(float)(*this)["SCALE Y"],(float)(*this)["SCALE Z"]);
			s="["+pos.x+";"+pos.y+";"+pos.z+"];";
			s=s+"["+rot.x+";"+rot.y+";"+rot.z+"];";
			s=s+"["+scale.x+";"+scale.y+";"+scale.z+"]";

			return s;
		}



		float4x4 get()
		{
			float3 pos,rot,scale;
			pos.set((float)(*this)["X"],(float)(*this)["Y"],(float)(*this)["Z"]);
			rot.set((float)(*this)["PITCH"],(float)(*this)["YAW"],(float)(*this)["ROLL"]);
			scale.set((float)(*this)["SCALE X"],(float)(*this)["SCALE Y"],(float)(*this)["SCALE Z"]);
			if (scale.x==0)
			{
				MessageBox::Show(
					"scale-t ne allits 0-ra", 
					"Warning", MessageBoxButtons::OK, 
					MessageBoxIcon::Exclamation);

				scale.x=1;
			}
			if (scale.y==0)
			{
				MessageBox::Show(
					"scale-t ne allits 0-ra", 
					"Warning", MessageBoxButtons::OK, 
					MessageBoxIcon::Exclamation);
				scale.y=1;
			}
			if (scale.z==0)
			{
				MessageBox::Show(
					"scale-t ne allits 0-ra", 
					"Warning", MessageBoxButtons::OK, 
					MessageBoxIcon::Exclamation);
				scale.z=1;
			}

			float4x4 mtx;
			posrotscale_to_float4x4(pos,rot,scale,mtx);

			return mtx;
		}
		void posrotscale_to_float4x4(const float3& pos, const float3& rot,const float3& scale,float4x4& mtx)
		{
			mtx._14=mtx._24=mtx._34=0;mtx._44=1;
			mtx.from_rotation(DEG2RAD*(rot.y),DEG2RAD*(rot.x),DEG2RAD*(rot.z));
			mtx.scale(scale.x,scale.y,scale.z);
			mtx.translate()=pos;
		}
		void float4x4_to_posrotscale(const float4x4& mat,float3& pos, float3& rot,float3& scale)
		{
			float4x4 mtx=mat;
			scale.x=length(mtx.axis(0));
			scale.y=length(mtx.axis(1));
			scale.z=length(mtx.axis(2));


			mtx.axis(0)/=scale.x;
			mtx.axis(1)/=scale.y;
			mtx.axis(2)/=scale.z;

			mtx.to_rotation(rot.y,rot.x,rot.z);
			rot.x*=RAD2DEG;
			rot.y*=RAD2DEG;
			rot.z*=RAD2DEG;

			pos=mtx.translate();

			pos.x=clamptodigit<3>(pos.x);
			pos.y=clamptodigit<3>(pos.y);
			pos.z=clamptodigit<3>(pos.z);

			rot.x=clamptodigit<3>(rot.x);
			rot.y=clamptodigit<3>(rot.y);
			rot.z=clamptodigit<3>(rot.z);

			scale.x=clamptodigit<3>(scale.x);
			scale.y=clamptodigit<3>(scale.y);
			scale.z=clamptodigit<3>(scale.z);
		}

		float4x4prop(MyPropertyTable^ p, entity_t* e):MyPropertyTable(p,e)
		{
			Properties->Add(gcnew PropertyGridEx::PropertySpec("X", Single::typeid, "Position",nullptr, 0.0f));
			Properties->Add(gcnew PropertyGridEx::PropertySpec("Y", Single::typeid, "Position",nullptr, 0.0f));
			Properties->Add(gcnew PropertyGridEx::PropertySpec("Z", Single::typeid, "Position",nullptr, 0.0f));
			Properties->Add(gcnew PropertyGridEx::PropertySpec("YAW", Single::typeid, "Rotation",nullptr, 0.0f));
			Properties->Add(gcnew PropertyGridEx::PropertySpec("PITCH", Single::typeid, "Rotation",nullptr, 0.0f));
			Properties->Add(gcnew PropertyGridEx::PropertySpec("ROLL", Single::typeid, "Rotation",nullptr, 0.0f));
			Properties->Add(gcnew PropertyGridEx::PropertySpec("SCALE X", Single::typeid, "Scale",nullptr, 1.0f));
			Properties->Add(gcnew PropertyGridEx::PropertySpec("SCALE Y", Single::typeid, "Scale",nullptr, 1.0f));
			Properties->Add(gcnew PropertyGridEx::PropertySpec("SCALE Z", Single::typeid, "Scale",nullptr, 1.0f));
			(*this)["X"]=0.0f;
			(*this)["Y"]=0.0f;
			(*this)["Z"]=0.0f;
			(*this)["YAW"]=0.0f;
			(*this)["PITCH"]=0.0f;
			(*this)["ROLL"]=0.0f;
			(*this)["SCALE X"]=1.0f;
			(*this)["SCALE Y"]=1.0f;
			(*this)["SCALE Z"]=1.0f;
		}

		float4x4prop(MyPropertyTable^ p, entity_t* e,float4x4 val):MyPropertyTable(p,e)
		{
			float3 pos,rot,scale;
			float4x4_to_posrotscale(val,pos,rot,scale);
			Properties->Add(gcnew PropertyGridEx::PropertySpec("X", Single::typeid, "Position", nullptr, pos.x));
			Properties->Add(gcnew PropertyGridEx::PropertySpec("Y", Single::typeid, "Position", nullptr, pos.y));
			Properties->Add(gcnew PropertyGridEx::PropertySpec("Z", Single::typeid, "Position", nullptr, pos.z));
			Properties->Add(gcnew PropertyGridEx::PropertySpec("YAW", Single::typeid, "Rotation", nullptr, rot.y));
			Properties->Add(gcnew PropertyGridEx::PropertySpec("PITCH", Single::typeid, "Rotation", nullptr, rot.x));
			Properties->Add(gcnew PropertyGridEx::PropertySpec("ROLL", Single::typeid, "Rotation", nullptr, rot.z));
			Properties->Add(gcnew PropertyGridEx::PropertySpec("SCALE X", Single::typeid, "Scale", nullptr, scale.x));
			Properties->Add(gcnew PropertyGridEx::PropertySpec("SCALE Y", Single::typeid, "Scale", nullptr, scale.y));
			Properties->Add(gcnew PropertyGridEx::PropertySpec("SCALE Z", Single::typeid, "Scale", nullptr, scale.z));
			(*this)["X"]=pos.x;
			(*this)["Y"]=pos.y;
			(*this)["Z"]=pos.z;
			(*this)["YAW"]=rot.y;
			(*this)["PITCH"]=rot.x;
			(*this)["ROLL"]=rot.z;
			(*this)["SCALE X"]=scale.x;
			(*this)["SCALE Y"]=scale.y;
			(*this)["SCALE Z"]=scale.z;
		}

		void set(const float4x4& val)
		{
			float3 pos,rot,scale;
			float4x4_to_posrotscale(val,pos,rot,scale);
			(*this)["X"]=pos.x;
			(*this)["Y"]=pos.y;
			(*this)["Z"]=pos.z;
			(*this)["YAW"]=rot.y;
			(*this)["PITCH"]=rot.x;
			(*this)["ROLL"]=rot.z;
			(*this)["SCALE X"]=scale.x;
			(*this)["SCALE Y"]=scale.y;
			(*this)["SCALE Z"]=scale.z;
		}

		void set(Transform^ t)
		{
			set(*t->mtx);
		}
	};





	public ref struct ColorProp:public ColorPicker::ColorWrapper
	{
	private:
		color4f* c;
		entity_t* entity;
		String^ prop_name;
	public:
		ColorProp(color4f* o,entity_t* e, String^ pn)
		{
			c=o;
			entity=e;
			prop_name=pn;
		}
		virtual String^ ToString() override
		{
			String^ s = gcnew String("");
			int x=int(255*c->x+0.49f);
			int y=int(255*c->y+0.49f);
			int z=int(255*c->z+0.49f);
			int w=int(255*c->w+0.49f);
			s+=x+";"+y+";"+z+";"+w;

			return s;
		}


		property System::Drawing::Color  color
		{
			virtual System::Drawing::Color get() override
			{
				return System::Drawing::Color::FromArgb((int)(c->w*255+0.49f),(int)(c->x*255+0.49f),(int)(c->y*255+0.49f),(int)(c->z*255+0.49f));
			}

			virtual void set(System::Drawing::Color color) override
			{
				c->x=color.R/255.0f;
				c->y=color.G/255.0f;
				c->z=color.B/255.0f;
				c->w=color.A/255.0f;
				char ch[16384];
				String_to_string(prop_name,ch);
				entity->onchangeproperty(stringhash::get(ch));
			}
		}
	};






void add_property_to_grid(entity_t* ent,node* i_node, String^ crname,MyPropertyTable^ prop_collection);
void set_property_in_grid(entity_t* ent,node* i_node, String^ crname,MyPropertyTable^ prop_collection);

public ref class UIColorEditor : public System::Drawing::Design::UITypeEditor
{
public:
	virtual UITypeEditorEditStyle GetEditStyle(
		ITypeDescriptorContext^ context) override 
	{
		if (context != nullptr)
			return UITypeEditorEditStyle::Modal;

		return UITypeEditorEditStyle::None;
	}



public: 
	[RefreshProperties(RefreshProperties::All)]
	virtual System::Object^ EditValue(ITypeDescriptorContext^ context,
		System::IServiceProvider^ provider, System::Object^ value) override ;
};



public ref class UITrackbarEditor : System::Drawing::Design::UITypeEditor
{
public: UITrackbarEditor()
	{
		_wfes=nullptr;
		_control=nullptr;
	}
	IWindowsFormsEditorService^ _wfes ;
private: TrackBar^ _control ;
		 String^ prop_name;
		 PropertyGridEx::TrackbarEditorAttr^ lattr;
public: virtual UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext^ context) override
	{
		return UITypeEditorEditStyle::DropDown;
	}

public: virtual System::Object^ EditValue(ITypeDescriptorContext^ context,
			System::IServiceProvider^ provider,
		System::Object^ value) override;

private: void ValueChanged(System::Object^ sender,EventArgs^ e);

};


public ref class UITextboxEditor : System::Drawing::Design::UITypeEditor
{
public: UITextboxEditor()
		{
			_wfes=nullptr;
			_control=nullptr;
		}
		IWindowsFormsEditorService^ _wfes ;
private: TextBox^ _control ;
public: virtual UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext^ context) override
		{
			return UITypeEditorEditStyle::DropDown;
		}

public: virtual System::Object^ EditValue(ITypeDescriptorContext^ context,
			System::IServiceProvider^ provider,
			System::Object^ value) override;

private: void Text_Changed(System::Object^ sender, EventArgs^ e);
};

public ref class UIStringlistEditor: System::Drawing::Design::UITypeEditor
{
public: UIStringlistEditor()
		{
			_wfes=nullptr;
			_control=nullptr;
		}
		IWindowsFormsEditorService^ _wfes ;
private: TextBox^ _control ;
public: virtual UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext^ context) override
		{
			return UITypeEditorEditStyle::Modal;
		}

public: virtual System::Object^ EditValue(ITypeDescriptorContext^ context,
			System::IServiceProvider^ provider,
			System::Object^ value) override;

};


}